﻿// 2023/11/14: 首个记录。基于v1 2023/11/1版本，增加分类置信度和描述信息字段，Distance改为可选字段
// 2025/3/31: 实现ToGeneralSample方法
// 2025/4/18: 增加仪表盘标识大类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ASEva;
using SO = System.Reflection.ObfuscationAttribute;

namespace ImageLabelSampleV2
{
    [SO]
    enum ImageLabelClass
    {
        Unknown = 0, // 未知

        Auto = 100, // General automobile / 机动车大类
        Car = 101, // 轿车
        Bus = 102, //大巴
        Midibus = 103, // 小巴
        Truck = 104, // 货车
        Van = 105, // 面包车
        Semitrailer = 106, // 货柜车
        Trailer = 107, // 拖车
        SUV = 108, // SUV
        TinyCar = 109, // 微型车

        Minor = 200, // General vulnerable road user / 弱势参与者大类
        Ped = 201, // Pedestrian / 行人
        Bicycle = 202, // 自行车
        Tricycle = 203, // 三轮车
        Motorbike = 204, // 摩托车
        Wheelchair = 205, // 轮椅
        Animal = 206, // 动物

        Static = 300, // General static object / 静态目标大类
        ZebraCrossing = 301, // 斑马线
        TrafficLight = 302, // 交通灯
        TrafficSign = 303, // 交通信号
        ParkingSlot = 304, // 车位
        Cone = 305, // 锥形路障
        ManholeCover = 306, // 井盖
        Patch = 307, // 路面补丁
        Gantry = 308, // 龙门架
        Pole = 309, // 竖杆
        Tree = 310, // 树木
        Vegetation = 311, // 灌木
        Building = 312, // 建筑物

        Dashboard = 1000, // Dashboard signal / 仪表盘标识大类
        Airbag = 1001, // 安全气囊
        ABS = 1002, // 防抱死制动
        BrakeSystemFailure = 1003, // 刹车系统故障
        ChargingSystemFailure = 1004, // 充电系统故障
        CentralWarningLamp = 1005, // 中央警告灯
        CoolingSystemFailure = 1006, // 冷却系统故障
        DoorsOpen = 1007, // 车门未关
        ElectronicPowerSteering = 1008, // 转向助力
        ElectronicStability = 1009, // 电子稳定性(ESC)
        EnginePreheating = 1010, // 发动机预热
        FrontFogLight = 1011, // 前雾灯
        LaneDepartureWarning = 1012, // 车道偏离预警
        LowBeam = 1013, // 近光灯
        LowEngineOilWarning = 1014, // 低燃油警告
        SeatBelt = 1015, // 安全带提醒
        SideLamp = 1016, // 示廓灯
        TyrePressureMonitoring = 1017, // 胎压异常
        WasherFluid = 1018, // 玻璃水不足
    }

    class ImageLabel
    {
        public int ID { get; set; } // 标签ID
        public ImageLabelClass Class { get; set; } // 标签类型
        public double? ClassConfidence { get; set; } // [%] Classification confidence / 分类置信度
        public double? Distance { get; set; } // [m] Average distance between the label's pixels to the camera (along x axis of camera coordinate system) / 标签距摄像头的平均距离（沿摄像头x轴方向）
        public FloatRect Rect { get; set; } // [pix] Border of the label / 标签矩形框
        public String Description { get; set; } // 描述信息
    }

    class ImageLabelSample : Sample
    {
        public IntSize ImageSize { get; set; } // [pix] 图像尺寸
        public List<ImageLabel> Labels { get; set; } // List of labels / 标签列表

        public ImageLabelSample()
        {
            Labels = new List<ImageLabel>();
        }

        public static string Title
        {
            get
            {
                return "Related video channel,Video frame width[pix],Video frame height[pix],Label count,Reserved 1,Reserved 2,Reserved 3,Reserved 4,First label's class,First label's ID,First label's distance[m],First label's top[pix],First label's bottom[pix],First label's left[pix],First label's right[pix],First label's class confidence[%],First label's description,First label's reserved 1,First label's reserved 2,First label's reserved 3,First label's reserved 4,First label's reserved 5,First label's reserved 6,First label's reserved 7,Second label's class,etc";
            }
        }

        public static string Protocol
        {
            get
            {
                return "image-label-sample-v2";
            }
        }

        public static string[] Protocols
        {
            get
            {
                return new string[] { "image-label-sample-v1", "image-label-sample-v2" };
            }
        }

        public override string GetGeneralSampleProtocol()
        {
            return Protocol;
        }

        public override string[] GetGeneralSampleProtocols()
        {
            return Protocols;
        }

        public override GeneralSample ToGeneralSample(int channel)
        {
            var sample = new GeneralSample();
            sample.SetTime(this);
            sample.Protocol = Protocol;
            sample.Channel = channel;

            // 8 fixed fields + 16 fields per label
            var data = new GeneralSampleValue[8 + 16 * Labels.Count];

            // 设置通道相关信息和图像尺寸
            data[0] = new GeneralSampleValue((double)channel); // 使用传入的通道值
            data[1] = new GeneralSampleValue((double)ImageSize.Width);
            data[2] = new GeneralSampleValue((double)ImageSize.Height);
            data[3] = new GeneralSampleValue((double)Labels.Count);
            // 保留字段4-7保持为空

            int baseIndex = 8;
            for (int i = 0; i < Labels.Count; i++)
            {
                var label = Labels[i];
                data[baseIndex + 0] = new GeneralSampleValue((double)(uint)label.Class);
                data[baseIndex + 1] = new GeneralSampleValue((double)label.ID);
                if (label.Distance != null) data[baseIndex + 2] = new GeneralSampleValue(label.Distance.Value);

                // 矩形坐标 - 与C++版本保持一致，顺序是: 左上角u, 左上角v, 右下角u, 右下角v
                data[baseIndex + 3] = new GeneralSampleValue((double)label.Rect.Left);
                data[baseIndex + 4] = new GeneralSampleValue((double)label.Rect.Top);
                data[baseIndex + 5] = new GeneralSampleValue((double)(label.Rect.Left + label.Rect.Width));
                data[baseIndex + 6] = new GeneralSampleValue((double)(label.Rect.Top + label.Rect.Height));

                // 分类置信度
                if (label.ClassConfidence != null) data[baseIndex + 7] = new GeneralSampleValue(label.ClassConfidence.Value);

                // 描述信息
                if (!string.IsNullOrEmpty(label.Description)) data[baseIndex + 8] = new GeneralSampleValue(label.Description);

                baseIndex += 16;
            }

            sample.NumberOfSignificants = data.Length;
            sample.Values = data.ToList();

            return sample;
        }

        public override bool FromGeneralSample(GeneralSample sample)
        {
            SetTime(sample);

            #region image-label-sample-v2
            if (sample.Protocol == "image-label-sample-v2")
            {
                var v = sample.Values.ToArray();
                if (v.Length < 8) return false;
                if (v[0].IsNotNumber() || v[0].number != sample.Channel.Value) return false;

                if (v[1].IsNotNumber() ||
                    v[2].IsNotNumber() ||
                    v[3].IsNotNumber()) return false;

                int width = (int)v[1].number;
                int height = (int)v[2].number;
                int nLabels = (int)v[3].number;
                if (v.Length != 8 + nLabels * 16) return false;

                ImageSize = new IntSize(width, height);

                Labels.Clear();
                for (int i = 0; i < nLabels; i++)
                {
                    var label = new ImageLabel();
                    label.Class = (ImageLabelClass)(int)v[16 * i + 8].number;
                    label.ID = (int)v[16 * i + 9].number;
                    label.Distance = v[16 * i + 10].ToDouble();

                    var left = (float)v[16 * i + 11].number;
                    var top = (float)v[16 * i + 12].number;
                    var right = (float)v[16 * i + 13].number;
                    var bottom = (float)v[16 * i + 14].number;
                    label.Rect = new FloatRect(left, top, right - left, bottom - top);

                    label.ClassConfidence = v[16 * i + 15].ToDouble();
                    if (v[16 * i + 16].IsText()) label.Description = v[16 * i + 16].text;

                    Labels.Add(label);
                }

                return true;
            }
            #endregion

            #region image-label-sample-v1
            else if (sample.Protocol == "image-label-sample-v1")
            {
                var v = sample.Values.ToArray();
                if (v.Length < 4) return false;
                if (v[0].IsNotNumber() || v[0].number != sample.Channel.Value) return false;

                if (v[1].IsNotNumber() ||
                    v[2].IsNotNumber() ||
                    v[3].IsNotNumber()) return false;

                int width = (int)v[1].number;
                int height = (int)v[2].number;
                int nLabels = (int)v[3].number;
                if (v.Length != 4 + nLabels * 7) return false;

                ImageSize = new IntSize(width, height);

                Labels.Clear();
                for (int i = 0; i < nLabels; i++)
                {
                    var label = new ImageLabel();
                    label.Class = (ImageLabelClass)(int)v[7 * i + 4].number;
                    label.ID = (int)v[7 * i + 5].number;
                    if (v[7 * i + 6].number > 0) label.Distance = v[7 * i + 6].number;

                    var left = (float)v[7 * i + 7].number;
                    var top = (float)v[7 * i + 8].number;
                    var right = (float)v[7 * i + 9].number;
                    var bottom = (float)v[7 * i + 10].number;
                    label.Rect = new FloatRect(left, top, right - left, bottom - top);

                    Labels.Add(label);
                }

                return true;
            }
            #endregion

            return false;
        }
    }
}
